//
// C++ Implementation: ProductDB
//
// Description: this class is a list containing Product objects parsed from files
//
//
// Author:  <>, (C) 2007
//
//
//

#include "ProductDB.h"




//creates an empty or fills it with products info from filename if filename is provided or not ""
ProductDB::ProductDB ( string fileName ) :list<Product>()
{
	if(fileName != "")
		operator >>(fileName,*this);
}


//returns whether the list is empty or not
//tip: when a loading from file has failed, the database remains unchanged
//so this basically that if the list was empty before trying to load something
//it remains empty after the loading failure
ProductDB::operator bool()
{
	return size()>0;
}

//parses the input stream into database
int parseToDb(istream& in, ProductDB& tmpDb)
{
	int count = 0;
	while (! in.eof() )
	{
		//here we use Product's constructor ability to feed itself by
		//eating one product from input file
		Product a(in); 
		if(a) //if the product is not undefined or empty of information
		{
			tmpDb.push_back (a);
			count ++;
		}
	}
	return count;
}


//Parses the given input stream in a view to replace the current list with the products in the stream
//If could parse without error, removes the products in memory, add those from the input stream
//and returns the number of products added.
//If found syntax errors in input stream, doesn't clear the current list and returns 0
int operator>> (istream& in, ProductDB& db)
{
	ProductDB tmp;
	int nbEaten = parseToDb(in,tmp);
	
	//tries to close the stream (will work if the file is a real file)
	try
	{
		ifstream* in_pt = (ifstream*)&in;
		in_pt->close();
	}
	catch(...)
	{
		cout << "file.close() in class ProductDB, given stream doesn't support that (not a file)" << endl;
	} //don't give too much importance to this
	//our istream is maybe not an ifstream
	db = tmp;
	return nbEaten;
}





//does the same actions as operator>> (istream&,ProductDB&)
//but creates the file input stream for us in background to pass it to the latter function
int operator>> (const char* filename, ProductDB& db)
{
	//cout << "operator >> " << filename << "..." << endl;
	ifstream tmpStream(filename);
	if(!tmpStream)
	{
		cout << "Could not open file " << filename << "." << endl;
		return 0;
	}
	else
	{
		return operator >>(tmpStream,db); //tries to feed the stream into us
	}
}




//same as above function but with string parameter
int operator>> (string& filename, ProductDB& db)
{
	return operator >>(filename.c_str(),db);
}





//lets us do cout << someProductDBInstance, which prints the latter's contents nicely
ostream& operator<< ( ostream& out, ProductDB& db )
{
	ProductDB::iterator it;
	out << "--<ProductDB>--" << endl;
	for ( it=db.begin();it!=db.end();it++ )
		out << *it;
	out << "--</ProductDB>--" << endl;
	return out;
}



//returns the list of products from database found to have a price higher than or equal to minValue
ProductDB ProductDB::getProductsMoreExpensive(float minValue)
{
	ProductDB tmp;
	ProductDB::iterator it;
	for(it=begin();it!=end();it++)
	{
		//if we find some object for a Selling price higher or equal to our minValue
		if(it->SellingPrice >= minValue)
		{
			tmp.push_back(*it);
		}
		//else : don't add it
	}
	return tmp;
}

//outputs to screen the profit in dollars expected if all the products' stocks were sold
void ProductDB::showTotalPredictedProfit()
{
	ProductDB::iterator it;
	float totalProfit=0;
	for(it=begin();it!=end();it++)
		totalProfit += it->getPredictedProfit();
	
	cout << "The predicted profit if all of the products were sold is:" << totalProfit << "$" << endl;
	if(totalProfit >= 0)
		cout << "In this situation, you would be making profit." << endl;
	else
		cout << "In this situation, you would end up losing money." << endl;
}


//copies this database back to a file using the usual representation (line breaks between elements)
bool ProductDB::writeTo_OtherFile(string& where)
{
	ofstream o(where.c_str());
	if(!o)
	{
		cout << "Could not open file "<<where<<" for writing." << endl;
		return false;
	}
	ProductDB::iterator it;
	for(it=begin();it!=end();it++)
	{
		o << it->name << endl << it->BuyingPrice << endl << it->SellingPrice << endl << it->quantity << endl;
	}
	o.close();
	cout << "Done copying database contents to file "<<where<<"." << endl;
	return true;
}



//outputs to screen the the profit that one would gain by selling out a given product
void ProductDB::showPredictedProfitOf (string& query)
{
	ProductDB::iterator it;
	float profit;
	bool found = false;
	for (it=begin(); it!=end(); it++)
	{
		if (it->name == query)
		{	
			found = true;	
			profit = it->getPredictedProfit();
			cout<< " * The predicted profit is of " << profit <<"$."<<endl;
			if (profit>=0)
				cout << "There is no loss."<<endl;
			else
				
				cout << "The price current price setup would make you lose money." << endl;
			break;
		}
	}
	if(!found)
	{
		cout << "There is no product named \"" << query << "\" in database. Giving up." << endl;
	}
}
	
//convenience function to show on a same line all the database's products' names.
void ProductDB::showNames()
{
	ProductDB::iterator it;
	ProductDB::iterator next;
	for (it=begin(); it!=end(); it++){
		next = it;
		next++;
		cout<<it->name<<(next!=end() ? ", " : ".");
	} 
}



//returns the list of the products found to have more units in stock than the given minValue
ProductDB ProductDB::getMoreNumerousProducts(float minValue)
{
	ProductDB tmp;
	ProductDB::iterator it;
	for(it=begin();it!=end();it++)
	{
		//if we find some object that have a quantity higher or equal to our minValue
		if(it->quantity >= minValue)
		{
			tmp.push_back(*it); // it adds it to the list
		}
		//else : don't add it
	}
	return tmp;
}
